#include "stdafx.h"
#include "Semaphore_.h"
#ifdef WIN32
# include <Windows.h>
#elif defined(__APPLE__) && defined(__MACH__)
# include <mach/task.h>
# include <utility/TimeSpec.h>
# include <errno.h>
# include <mach/semaphore.h>
#else
# include <utility/TimeSpec.h>
# include <errno.h>
#endif

namespace serika
{
	namespace threading
	{
		Semaphore::Semaphore(int initValue)
		{
	#ifdef WIN32
			mSem = ::CreateSemaphore(0, initValue, 0x7FFFFFFF, 0);
	#else
			sem_init(&mSem, 0, initValue);
	#endif
		}
	
		Semaphore::~Semaphore()
		{
	#ifdef WIN32
			::CloseHandle(mSem);
	#else
			sem_destroy(&mSem);
	#endif
		}
	
		LockState::Enum Semaphore::Acquire(long timeout)
		{
	#ifdef WIN32
			DWORD ret = ::WaitForSingleObject(mSem, timeout);
			switch (ret)
			{
			case WAIT_OBJECT_0:
				return LockState::SUCCEEDED;
			case WAIT_TIMEOUT:
				return LockState::TIMEDOUT;
			case WAIT_ABANDONED:
				return LockState::OWNERTERM;
			default:
				return LockState::FAILED;
			}
	#else
			int ret = 0;
			if (timeout == -1)
			{
				ret = sem_wait(&mSem);
			}
			else
			{
				ut::TimeSpec t = ut::TimeSpec::MakeTimeout(timeout);
#if defined(__APPLE__) && defined(__MACH__)
				ret = semaphore_timedwait(mSem, *reinterpret_cast<mach_timespec_t*>(&t));
#else
				ret = sem_timedwait(&mSem, &t);
#endif
			}
			if (ret == 0)
			{
				return LockState::SUCCEEDED;
			}
			else 
			{
				switch (errno)
				{
				case ETIMEDOUT:
					return LockState::TIMEDOUT;
				default:
					return LockState::FAILED;
				}
			}
	#endif
		}
	
		LockState::Enum Semaphore::TryAcquire()
		{
	#ifdef WIN32
			DWORD ret = ::WaitForSingleObject(mSem, 0);
			switch (ret)
			{
			case WAIT_OBJECT_0:
				return LockState::SUCCEEDED;
			case WAIT_TIMEOUT:
				return LockState::TIMEDOUT;
			case WAIT_ABANDONED:
				return LockState::OWNERTERM;
			default:
				return LockState::FAILED;
			}
	#else
			int ret = sem_trywait(&mSem);
			if (ret == 0)
			{
				return LockState::SUCCEEDED;
			}
			else
			{
				switch (errno)
				{
				case 0:
					return LockState::SUCCEEDED;
				case ETIMEDOUT:
					return LockState::TIMEDOUT;
				default:
					return LockState::FAILED;
				}
			}
	#endif
		}
	
		void Semaphore::Release()
		{
	#ifdef WIN32
			::ReleaseSemaphore(mSem, 1, 0);
	#else
			sem_post(&mSem);
	#endif
		}
	}
}
	
